// Copyright 2019 Google LLC.
#ifndef TextStyle_DEFINED
#define TextStyle_DEFINED

#include <native_drawing/drawing_text_declaration.h>
#include <native_drawing/drawing_text_typography.h>
#include <optional>
#include <vector>
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontMetrics.h"
#include "include/core/SkFontStyle.h"
#include "include/core/SkPaint.h"
#include "include/core/SkScalar.h"
#include "modules/skparagraph/include/DartTypes.h"
#include "modules/skparagraph/include/FontArguments.h"
#include "modules/skparagraph/include/FontRastrSettings.h"
#include "modules/skparagraph/include/ParagraphPainter.h"
#include "modules/skparagraph/include/TextShadow.h"
#include "src/core/SkTraceEvent.h"
#include "src/core/SkTraceEventCommon.h"

// TODO: Make it external so the other platforms (Android) could use it
#define DEFAULT_FONT_FAMILY "sans-serif"

namespace skia {
namespace textlayout {

static inline bool nearlyZero(SkScalar x, SkScalar tolerance = SK_ScalarNearlyZero) {
    if (SkScalarIsFinite(x)) {
        return SkScalarNearlyZero(x, tolerance);
    }
    return false;
}

static inline bool nearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance = SK_ScalarNearlyZero) {
    if (SkScalarIsFinite(x) && SkScalarIsFinite(x)) {
        return SkScalarNearlyEqual(x, y, tolerance);
    }
    // Inf == Inf, anything else is false
    return x == y;
}

// Multiple decorations can be applied at once. Ex: Underline and overline is
// (0x1 | 0x2)
enum TextDecoration {
    kNoDecoration = 0x0,
    kUnderline = 0x1,
    kOverline = 0x2,
    kLineThrough = 0x4,
};
constexpr TextDecoration AllTextDecorations[] = {
        kNoDecoration,
        kUnderline,
        kOverline,
        kLineThrough,
};

enum TextDecorationStyle { kSolid, kDouble, kDotted, kDashed, kWavy };

enum TextDecorationMode { kGaps, kThrough };

enum StyleType {
    kNone,
    kAllAttributes,
    kFont,
    kForeground,
    kBackground,
    kShadow,
    kDecorations,
    kLetterSpacing,
    kWordSpacing
};

struct Decoration {
    TextDecoration fType;
    TextDecorationMode fMode;
    SkColor fColor;
    TextDecorationStyle fStyle;
    SkScalar fThicknessMultiplier;

    bool operator==(const Decoration& other) const {
        return this->fType == other.fType &&
               this->fMode == other.fMode &&
               this->fColor == other.fColor &&
               this->fStyle == other.fStyle &&
               this->fThicknessMultiplier == other.fThicknessMultiplier;
    }
};

/// Where to vertically align the placeholder relative to the surrounding text.
enum class PlaceholderAlignment {
  /// Match the baseline of the placeholder with the baseline.
  kBaseline,

  /// Align the bottom edge of the placeholder with the baseline such that the
  /// placeholder sits on top of the baseline.
  kAboveBaseline,

  /// Align the top edge of the placeholder with the baseline specified in
  /// such that the placeholder hangs below the baseline.
  kBelowBaseline,

  /// Align the top edge of the placeholder with the top edge of the font.
  /// When the placeholder is very tall, the extra space will hang from
  /// the top and extend through the bottom of the line.
  kTop,

  /// Align the bottom edge of the placeholder with the top edge of the font.
  /// When the placeholder is very tall, the extra space will rise from
  /// the bottom and extend through the top of the line.
  kBottom,

  /// Align the middle of the placeholder with the middle of the text. When the
  /// placeholder is very tall, the extra space will grow equally from
  /// the top and bottom of the line.
  kMiddle,
};

struct FontFeature {
    FontFeature(const SkString name, int value) : fName(name), fValue(value) {}
    bool operator==(const FontFeature& that) const {
        return fName == that.fName && fValue == that.fValue;
    }
    SkString fName;
    int fValue;
};

struct PlaceholderStyle {
    PlaceholderStyle() = default;
    PlaceholderStyle(SkScalar width, SkScalar height, PlaceholderAlignment alignment,
                     TextBaseline baseline, SkScalar offset)
            : fWidth(width)
            , fHeight(height)
            , fAlignment(alignment)
            , fBaseline(baseline)
            , fBaselineOffset(offset) {}

    bool equals(const PlaceholderStyle&) const;

    SkScalar fWidth = 0;
    SkScalar fHeight = 0;
    PlaceholderAlignment fAlignment = PlaceholderAlignment::kBaseline;
    TextBaseline fBaseline = TextBaseline::kAlphabetic;
    // Distance from the top edge of the rect to the baseline position. This
    // baseline will be aligned against the alphabetic baseline of the surrounding
    // text.
    //
    // Positive values drop the baseline lower (positions the rect higher) and
    // small or negative values will cause the rect to be positioned underneath
    // the line. When baseline == height, the bottom edge of the rect will rest on
    // the alphabetic baseline.
    SkScalar fBaselineOffset = 0;
    
    OH_Drawing_PlaceholderSpan toOHPlaceHolder() const {
        OH_Drawing_PlaceholderSpan ret;
        ret.width = fWidth;
        ret.height = fHeight;
        static_assert((int)PlaceholderAlignment::kBaseline == OH_Drawing_PlaceholderVerticalAlignment::ALIGNMENT_OFFSET_AT_BASELINE, "PlaceholderAlignment is not equal!");
        static_assert((int)PlaceholderAlignment::kAboveBaseline == OH_Drawing_PlaceholderVerticalAlignment::ALIGNMENT_ABOVE_BASELINE, "PlaceholderAlignment is not equal!");
        static_assert((int)PlaceholderAlignment::kBelowBaseline == OH_Drawing_PlaceholderVerticalAlignment::ALIGNMENT_BELOW_BASELINE, "PlaceholderAlignment is not equal!");
        static_assert((int)PlaceholderAlignment::kTop == OH_Drawing_PlaceholderVerticalAlignment::ALIGNMENT_TOP_OF_ROW_BOX, "PlaceholderAlignment is not equal!");
        static_assert((int)PlaceholderAlignment::kBottom == OH_Drawing_PlaceholderVerticalAlignment::ALIGNMENT_BOTTOM_OF_ROW_BOX, "PlaceholderAlignment is not equal!");
        static_assert((int)PlaceholderAlignment::kMiddle == OH_Drawing_PlaceholderVerticalAlignment::ALIGNMENT_CENTER_OF_ROW_BOX, "PlaceholderAlignment is not equal!");
        ret.alignment = (OH_Drawing_PlaceholderVerticalAlignment)fAlignment;
        ret.baseline = (OH_Drawing_TextBaseline)fBaseline;
        ret.baselineOffset = fBaselineOffset;
        return ret;
    }
};

class TextStyle {
private:
    struct DecorationMapping {
        TextDecoration skiaDecoration;
        OH_Drawing_TextDecoration ohosDecoration;
    };
    
    static constexpr DecorationMapping kDecorationMappings[] = {
        { TextDecoration::kUnderline, TEXT_DECORATION_UNDERLINE },
        { TextDecoration::kOverline, TEXT_DECORATION_OVERLINE },
        { TextDecoration::kLineThrough, TEXT_DECORATION_LINE_THROUGH }
    };
    
public:
    TextStyle() {
        fTextStyleOHOS = OH_Drawing_CreateTextStyle();
    }
    ~TextStyle() {
        if (fTextStyleOHOS) {
            OH_Drawing_DestroyTextStyle(fTextStyleOHOS);
        }
        for (auto shadow : fTextShadowsOHOS) {
            if (shadow) {
                OH_Drawing_DestroyTextShadow(shadow);
            }
        }
    }
    TextStyle(const TextStyle& other) {
        TRACE_EVENT0("skia", "TextStyle::copy");
        CopyNormalStyle(other);
        this->fTextStyleOHOS = OH_Drawing_CreateTextStyle();
        fillOHOSStyle();
    }
    TextStyle& operator=(const TextStyle& other) {
        TRACE_EVENT0("skia", "TextStyle::copy");
        CopyNormalStyle(other);
        fillOHOSStyle();
        return *this;
    }

    TextStyle(TextStyle&& other) {
        TRACE_EVENT0("skia", "TextStyle::move");
        MoveNormalStyle(other);
        this->fTextStyleOHOS = other.fTextStyleOHOS;
        other.fTextStyleOHOS = nullptr;
    }
    TextStyle& operator=(TextStyle&& other) {
        TRACE_EVENT0("skia", "TextStyle::move");
        MoveNormalStyle(other);
        if (fTextStyleOHOS) {
            OH_Drawing_DestroyTextStyle(fTextStyleOHOS);
        }
        fTextStyleOHOS = other.fTextStyleOHOS;
        other.fTextStyleOHOS = nullptr;
        return *this;
    }
    operator OH_Drawing_TextStyle*() const {
        return fTextStyleOHOS;
    }

    TextStyle cloneForPlaceholder();

    bool equals(const TextStyle& other) const;
    bool equalsByFonts(const TextStyle& that) const;
    bool matchOneAttribute(StyleType styleType, const TextStyle& other) const;
    bool operator==(const TextStyle& rhs) const { return this->equals(rhs); }

    // Colors
    SkColor getColor() const { return fColor; }
    void setColor(SkColor color) {
        fColor = color;
        OH_Drawing_SetTextStyleColor(fTextStyleOHOS, fColor);
    }

    bool hasForeground() const { return fHasForeground; }
    SkPaint getForeground() const {
        const SkPaint* paint = std::get_if<SkPaint>(&fForeground);
        return paint ? *paint : SkPaint();
    }
    ParagraphPainter::SkPaintOrID getForegroundPaintOrID() const {
        return fForeground;
    }
    void setForegroundPaint(SkPaint paint) {
        fHasForeground = true;
        fForeground = std::move(paint);
        SkPaint* paintPtr = std::get_if<SkPaint>(&fForeground);
        if (paintPtr) {
            paintPtr->updateOHPainter();
        }
        OH_Drawing_SetTextStyleForegroundBrush(fTextStyleOHOS, std::get<SkPaint>(fForeground));
        OH_Drawing_SetTextStyleForegroundPen(fTextStyleOHOS, std::get<SkPaint>(fForeground));
    }
    // DEPRECATED: prefer `setForegroundPaint`.
    void setForegroundColor(SkPaint paint) { setForegroundPaint(paint); }
    // Set the foreground to a paint ID.  This is intended for use by clients
    // that implement a custom ParagraphPainter that can not accept an SkPaint.
    void setForegroundPaintID(ParagraphPainter::PaintID paintID) {
        fHasForeground = true;
        fForeground = paintID;
    }
    void clearForegroundColor() { fHasForeground = false; }

    bool hasBackground() const { return fHasBackground; }
    SkPaint getBackground() const {
        const SkPaint* paint = std::get_if<SkPaint>(&fBackground);
        return paint ? *paint : SkPaint();
    }
    ParagraphPainter::SkPaintOrID getBackgroundPaintOrID() const {
        return fBackground;
    }
    void setBackgroundPaint(SkPaint paint) {
        paint.updateOHPainter();
        fHasBackground = true;
        fBackground = std::move(paint);
        SkPaint* paintPtr = std::get_if<SkPaint>(&fBackground);
        if (paintPtr) {
            paintPtr->updateOHPainter();
        }
        OH_Drawing_SetTextStyleBackgroundBrush(fTextStyleOHOS, std::get<SkPaint>(fBackground));
        OH_Drawing_SetTextStyleBackgroundPen(fTextStyleOHOS, std::get<SkPaint>(fBackground));
    }
    // DEPRECATED: prefer `setBackgroundPaint`.
    void setBackgroundColor(SkPaint paint) { setBackgroundPaint(paint); }
    void setBackgroundPaintID(ParagraphPainter::PaintID paintID) {
        fHasBackground = true;
        fBackground = paintID;
    }
    void clearBackgroundColor() { fHasBackground = false; }

    // Decorations
    Decoration getDecoration() const { return fDecoration; }
    TextDecoration getDecorationType() const { return fDecoration.fType; }
    TextDecorationMode getDecorationMode() const { return fDecoration.fMode; }
    SkColor getDecorationColor() const { return fDecoration.fColor; }
    TextDecorationStyle getDecorationStyle() const { return fDecoration.fStyle; }
    SkScalar getDecorationThicknessMultiplier() const {
        return fDecoration.fThicknessMultiplier;
    }

   void setDecoration(TextDecoration decoration) {
        if (decoration == TextDecoration::kNoDecoration) {
            return;
        }
        if (fTextStyleOHOS == nullptr) {
            return;
        }
        
        fDecoration.fType = decoration;
        static_assert((int)TextDecoration::kNoDecoration == OH_Drawing_TextDecoration::TEXT_DECORATION_NONE, "TextDecoration not equal!");
        static_assert((int)TextDecoration::kUnderline == OH_Drawing_TextDecoration::TEXT_DECORATION_UNDERLINE, "TextDecoration not equal!");
        static_assert((int)TextDecoration::kOverline == OH_Drawing_TextDecoration::TEXT_DECORATION_OVERLINE, "TextDecoration not equal!");
        static_assert((int)TextDecoration::kLineThrough == OH_Drawing_TextDecoration::TEXT_DECORATION_LINE_THROUGH, "TextDecoration not equal!");
        
        OH_Drawing_SetTextStyleDecoration(fTextStyleOHOS, OH_Drawing_TextDecoration::TEXT_DECORATION_NONE);
        for (const auto& mapping : kDecorationMappings) {
            if ((decoration & mapping.skiaDecoration) != 0) {
                OH_Drawing_AddTextStyleDecoration(fTextStyleOHOS, mapping.ohosDecoration);
            }
        }
    }
    
    void setDecorationMode(TextDecorationMode mode) { fDecoration.fMode = mode; } // OH do not have DecorationMode
    void setDecorationStyle(TextDecorationStyle style) {
        fDecoration.fStyle = style;
        static_assert((int)TextDecorationStyle::kSolid == OH_Drawing_TextDecorationStyle::TEXT_DECORATION_STYLE_SOLID, "TextDecorationStyle not equal!");
        static_assert((int)TextDecorationStyle::kDouble == OH_Drawing_TextDecorationStyle::TEXT_DECORATION_STYLE_DOUBLE, "TextDecorationStyle not equal!");
        static_assert((int)TextDecorationStyle::kDotted == OH_Drawing_TextDecorationStyle::TEXT_DECORATION_STYLE_DOTTED, "TextDecorationStyle not equal!");
        static_assert((int)TextDecorationStyle::kDashed == OH_Drawing_TextDecorationStyle::TEXT_DECORATION_STYLE_DASHED, "TextDecorationStyle not equal!");
        static_assert((int)TextDecorationStyle::kWavy == OH_Drawing_TextDecorationStyle::TEXT_DECORATION_STYLE_WAVY, "TextDecorationStyle not equal!");
        OH_Drawing_SetTextStyleDecorationStyle(fTextStyleOHOS, (OH_Drawing_TextDecorationStyle)style);
    }
    void setDecorationColor(SkColor color) {
        fDecoration.fColor = color;
        OH_Drawing_SetTextStyleDecorationColor(fTextStyleOHOS, color);
    }
    void setDecorationThicknessMultiplier(SkScalar m) {
        fDecoration.fThicknessMultiplier = m;
        OH_Drawing_SetTextStyleDecorationThicknessScale(fTextStyleOHOS, m);
    }

    // Weight/Width/Slant
    SkFontStyle getFontStyle() const { return fFontStyle; }
    static OH_Drawing_FontWeight toOHFontWeight(SkFontStyle fontStyle) {
        switch (fontStyle.weight()) {
            case (fontStyle.kInvisible_Weight):
            case (fontStyle.kThin_Weight):
                return FONT_WEIGHT_100;
            case (fontStyle.kExtraLight_Weight):
                return FONT_WEIGHT_200;
            case (fontStyle.kLight_Weight):
                return FONT_WEIGHT_300;
            case (fontStyle.kNormal_Weight):
                return FONT_WEIGHT_400;
            case (fontStyle.kMedium_Weight):
                return FONT_WEIGHT_500;
            case (fontStyle.kSemiBold_Weight):
                return FONT_WEIGHT_600;
            case (fontStyle.kBold_Weight):
                return FONT_WEIGHT_700;
            case (fontStyle.kExtraBold_Weight):
                return FONT_WEIGHT_800;
            case (fontStyle.kBlack_Weight):
            case (fontStyle.kExtraBlack_Weight):
                return FONT_WEIGHT_900;
            default:
                return FONT_WEIGHT_400;
        }
    }
    static SkFontStyle::Weight toSkFontWeight(OH_Drawing_FontWeight fontWeight) {
        switch (fontWeight) {
            case FONT_WEIGHT_100:
                return SkFontStyle::kThin_Weight;
            case FONT_WEIGHT_200:
                return SkFontStyle::kExtraLight_Weight;
            case FONT_WEIGHT_300:
                return SkFontStyle::kLight_Weight;
            case FONT_WEIGHT_400:
                return SkFontStyle::kNormal_Weight;
            case FONT_WEIGHT_500:
                return SkFontStyle::kMedium_Weight;
            case FONT_WEIGHT_600:
                return SkFontStyle::kSemiBold_Weight;
            case FONT_WEIGHT_700:
                return SkFontStyle::kBold_Weight;
            case FONT_WEIGHT_800:
                return SkFontStyle::kExtraBold_Weight;
            case FONT_WEIGHT_900:
                return SkFontStyle::kBlack_Weight;
            default:
                return SkFontStyle::kNormal_Weight;
        }
    }
    static OH_Drawing_FontStyleStruct toOHFontStyle(SkFontStyle fontStyle) {
        OH_Drawing_FontStyleStruct style;
        style.slant = (OH_Drawing_FontStyle)fontStyle.slant();
        style.width = (OH_Drawing_FontWidth)fontStyle.width();
        style.weight = toOHFontWeight(fontStyle);
        return style;
    }
    void setFontStyle(SkFontStyle fontStyle) {
        fFontStyle = fontStyle;

        static_assert((int)SkFontStyle::kUpright_Slant == OH_Drawing_FontStyle::FONT_STYLE_NORMAL, "FontStyle not equal!");
        static_assert((int)SkFontStyle::kItalic_Slant == OH_Drawing_FontStyle::FONT_STYLE_ITALIC, "FontStyle not equal!");
        static_assert((int)SkFontStyle::kOblique_Slant == OH_Drawing_FontStyle::FONT_STYLE_OBLIQUE, "FontStyle not equal!");
        static_assert((int)SkFontStyle::kUltraCondensed_Width == OH_Drawing_FontWidth::FONT_WIDTH_ULTRA_CONDENSED, "FontWidth not equal!");
        static_assert((int)SkFontStyle::kExtraCondensed_Width == OH_Drawing_FontWidth::FONT_WIDTH_EXTRA_CONDENSED, "FontWidth not equal!");
        static_assert((int)SkFontStyle::kCondensed_Width == OH_Drawing_FontWidth::FONT_WIDTH_CONDENSED, "FontWidth not equal!");
        static_assert((int)SkFontStyle::kSemiCondensed_Width == OH_Drawing_FontWidth::FONT_WIDTH_SEMI_CONDENSED, "FontWidth not equal!");
        static_assert((int)SkFontStyle::kNormal_Width == OH_Drawing_FontWidth::FONT_WIDTH_NORMAL, "FontWidth not equal!");
        static_assert((int)SkFontStyle::kSemiExpanded_Width == OH_Drawing_FontWidth::FONT_WIDTH_SEMI_EXPANDED, "FontWidth not equal!");
        static_assert((int)SkFontStyle::kExpanded_Width == OH_Drawing_FontWidth::FONT_WIDTH_EXPANDED, "FontWidth not equal!");
        static_assert((int)SkFontStyle::kExtraExpanded_Width == OH_Drawing_FontWidth::FONT_WIDTH_EXTRA_EXPANDED, "FontWidth not equal!");
        static_assert((int)SkFontStyle::kUltraExpanded_Width == OH_Drawing_FontWidth::FONT_WIDTH_ULTRA_EXPANDED, "FontWidth not equal!");
        
        OH_Drawing_SetTextStyleFontStyleStruct(fTextStyleOHOS, toOHFontStyle(fontStyle));
    }

    // Shadows
    size_t getShadowNumber() const { return fTextShadows.size(); }
    std::vector<TextShadow> getShadows() const { return fTextShadows; }
    void addShadowOHOS(const TextShadow &shadow) {
        auto shadowOhos = OH_Drawing_CreateTextShadow();
        OH_Drawing_SetTextShadow(shadowOhos, shadow.fColor, (OH_Drawing_Point *)&shadow, shadow.fBlurSigma);
        OH_Drawing_TextStyleAddShadow(fTextStyleOHOS, shadowOhos);
        fTextShadowsOHOS.emplace_back(shadowOhos);
    }
    void addShadow(TextShadow shadow) {
        fTextShadows.emplace_back(shadow);
        TextShadow& storedShadow = fTextShadows.back();
        addShadowOHOS(storedShadow);
    }
    void resetShadows() {
        fTextShadows.clear();
        OH_Drawing_TextStyleClearShadows(fTextStyleOHOS);
        for (auto shadow : fTextShadowsOHOS) {
            OH_Drawing_DestroyTextShadow(shadow);
        }
        fTextShadowsOHOS.clear();
    }
    

    // Font features
    size_t getFontFeatureNumber() const { return fFontFeatures.size(); }
    std::vector<FontFeature> getFontFeatures() const { return fFontFeatures; }
    void addFontFeatureOHOS(const FontFeature &fontFeature) {
        OH_Drawing_TextStyleAddFontFeature(fTextStyleOHOS, fontFeature.fName.c_str(), fontFeature.fValue);
    }
    void addFontFeature(const SkString& fontFeature, int value) {
        fFontFeatures.emplace_back(fontFeature, value);
        addFontFeatureOHOS(fFontFeatures.back());
    }
    void resetFontFeatures() {
        fFontFeatures.clear();
        OH_Drawing_TextStyleClearFontFeature(fTextStyleOHOS);
    }

    // Font arguments
    const std::optional<FontArguments>& getFontArguments() const { return fFontArguments; }
    // The contents of the SkFontArguments will be copied into the TextStyle,
    // and the SkFontArguments can be safely deleted after setFontArguments returns.
    void setFontArguments(const std::optional<SkFontArguments>& args);

    SkScalar getFontSize() const { return fFontSize; }
    void setFontSize(SkScalar size) {
        fFontSize = size;
        OH_Drawing_SetTextStyleFontSize(fTextStyleOHOS, fFontSize);
    }

    const std::vector<SkString>& getFontFamilies() const { return fFontFamilies; }
    void setFontFamilies(std::vector<SkString> families) {
        fFontFamilies = std::move(families);
        fFontFamiliesOHOS.clear();
        for (const auto& str : fFontFamilies) {
            fFontFamiliesOHOS.push_back(str.c_str());
        }
        OH_Drawing_SetTextStyleFontFamilies(fTextStyleOHOS, fFontFamiliesOHOS.size(), fFontFamiliesOHOS.data());
    }

    SkScalar getBaselineShift() const { return fBaselineShift; }
    void setBaselineShift(SkScalar baselineShift) {
        fBaselineShift = baselineShift;
        OH_Drawing_TextStyleSetBaselineShift(fTextStyleOHOS, baselineShift);
    }

    void setHeight(SkScalar height) {
        fHeight = height;
        OH_Drawing_SetTextStyleFontHeight(fTextStyleOHOS, height);
    }
    SkScalar getHeight() const { return fHeightOverride ? fHeight : 0; }

    void setHeightOverride(bool heightOverride) { fHeightOverride = heightOverride; }
    bool getHeightOverride() const { return fHeightOverride; }

    void setHalfLeading(bool halfLeading) {
        fHalfLeading = halfLeading;
        OH_Drawing_SetTextStyleHalfLeading(fTextStyleOHOS, halfLeading);
    }
    bool getHalfLeading() const { return fHalfLeading; }

    void setLetterSpacing(SkScalar letterSpacing) {
        fLetterSpacing = letterSpacing;
        OH_Drawing_SetTextStyleLetterSpacing(fTextStyleOHOS, letterSpacing);
    }
    SkScalar getLetterSpacing() const { return fLetterSpacing; }

    void setWordSpacing(SkScalar wordSpacing) {
        fWordSpacing = wordSpacing;
        OH_Drawing_SetTextStyleWordSpacing(fTextStyleOHOS, wordSpacing);
    }
    SkScalar getWordSpacing() const { return fWordSpacing; }

    SkTypeface* getTypeface() const { return fTypeface.get(); }
    sk_sp<SkTypeface> refTypeface() const { return fTypeface; }
    void setTypeface(sk_sp<SkTypeface> typeface) { fTypeface = std::move(typeface); }

    const FontRastrSettings& getFontRastrSettings() const { return fFontRastrSettings; }
    void setFontRastrSettings(FontRastrSettings fontRastrSettings) { fFontRastrSettings = fontRastrSettings; }

    SkString getLocale() const { return fLocale; }
    void setLocale(const SkString& locale) {
        fLocale = locale;
        OH_Drawing_SetTextStyleLocale(fTextStyleOHOS, locale.c_str());
    }

    TextBaseline getTextBaseline() const { return fTextBaseline; }
    void setTextBaseline(TextBaseline baseline) {
        fTextBaseline = baseline;
        static_assert((int)TextBaseline::kAlphabetic == OH_Drawing_TextBaseline::TEXT_BASELINE_ALPHABETIC, "TextBaseline not equal!");
        static_assert((int)TextBaseline::kIdeographic == OH_Drawing_TextBaseline::TEXT_BASELINE_IDEOGRAPHIC, "TextBaseline not equal!");
        OH_Drawing_SetTextStyleBaseLine(fTextStyleOHOS, (OH_Drawing_TextBaseline)baseline);
    }

    void getFontMetrics(SkFontMetrics* metrics) const;

    bool isPlaceholder() const {
        return OH_Drawing_TextStyleIsPlaceholder(fTextStyleOHOS);
//         return fIsPlaceholder;
    }
    void setPlaceholder() {
        fIsPlaceholder = true;
        OH_Drawing_TextStyleSetPlaceholder(fTextStyleOHOS);
    }

private:
//    static const std::vector<SkString>* kDefaultFontFamilies;

    Decoration fDecoration = {
            TextDecoration::kNoDecoration,
            // TODO: switch back to kGaps when (if) switching flutter to skparagraph
            TextDecorationMode::kThrough,
            // It does not make sense to draw a transparent object, so we use this as a default
            // value to indicate no decoration color was set.
            SK_ColorTRANSPARENT, TextDecorationStyle::kSolid,
            // Thickness is applied as a multiplier to the default thickness of the font.
            1.0f};
    void fillOHOSStyle();
    void CopyNormalStyle(const TextStyle& src) {
        fColor = src.fColor;
        fFontSize = src.fFontSize;
        fFontFamilies = src.fFontFamilies;
        fDecoration = src.fDecoration;
        fHasBackground = src.fHasBackground;
        fHasForeground = src.fHasForeground;
        fBackground = src.fBackground;
        fForeground = src.fForeground;
        fHeightOverride = src.fHeightOverride;
        fFontFeatures = src.fFontFeatures;
        fHalfLeading = src.fHalfLeading;
        fBaselineShift = src.fBaselineShift;
        fFontArguments = src.fFontArguments;
        fFontStyle = src.fFontStyle;
        fHeight = src.fHeight;
        fLocale = src.fLocale;
        fLetterSpacing = src.fLetterSpacing;
        fWordSpacing = src.fWordSpacing;
        fTextBaseline = src.fTextBaseline;
        fTextShadows = src.fTextShadows;
        fTypeface = src.fTypeface;
        fIsPlaceholder = src.fIsPlaceholder;
    }
    void MoveNormalStyle(const TextStyle& src) {
        fColor = std::move(src.fColor);
        fFontSize = std::move(src.fFontSize);
        fFontFamilies = std::move(src.fFontFamilies);
        fDecoration = std::move(src.fDecoration);
        fHasBackground = std::move(src.fHasBackground);
        fHasForeground = std::move(src.fHasForeground);
        fBackground = std::move(src.fBackground);
        fForeground = std::move(src.fForeground);
        fHeightOverride = std::move(src.fHeightOverride);
        fFontFeatures = std::move(src.fFontFeatures);
        fHalfLeading = std::move(src.fHalfLeading);
        fBaselineShift = std::move(src.fBaselineShift);
        fFontArguments = std::move(src.fFontArguments);
        fFontStyle = std::move(src.fFontStyle);
        fHeight = std::move(src.fHeight);
        fLocale = std::move(src.fLocale);
        fLetterSpacing = std::move(src.fLetterSpacing);
        fWordSpacing = std::move(src.fWordSpacing);
        fTextBaseline = std::move(src.fTextBaseline);
        fTextShadows = std::move(src.fTextShadows);
        fTextShadowsOHOS = std::move(src.fTextShadowsOHOS);
        fTypeface = std::move(src.fTypeface);
        fIsPlaceholder = std::move(src.fIsPlaceholder);
    }
    OH_Drawing_TextStyle *fTextStyleOHOS = nullptr;
    SkFontStyle fFontStyle;

    std::vector<SkString> fFontFamilies{SkString(DEFAULT_FONT_FAMILY)};
    std::vector<const char*> fFontFamiliesOHOS;

    SkScalar fFontSize = 14.0;
    SkScalar fHeight = 1.0;
    bool fHeightOverride = false;
    SkScalar fBaselineShift = 0.0f;
    // true: half leading.
    // false: scale ascent/descent with fHeight.
    bool fHalfLeading = false;
    SkString fLocale = {};
    SkScalar fLetterSpacing = 0.0;
    SkScalar fWordSpacing = 0.0;

    TextBaseline fTextBaseline = TextBaseline::kAlphabetic;

    SkColor fColor = SK_ColorWHITE;
    bool fHasBackground = false;
    ParagraphPainter::SkPaintOrID fBackground;
    bool fHasForeground = false;
    ParagraphPainter::SkPaintOrID fForeground;

    std::vector<TextShadow> fTextShadows;
    std::vector<OH_Drawing_TextShadow*> fTextShadowsOHOS;

    sk_sp<SkTypeface> fTypeface;
    bool fIsPlaceholder = false;
    FontRastrSettings fFontRastrSettings;

    std::vector<FontFeature> fFontFeatures;

    std::optional<FontArguments> fFontArguments;
};

typedef size_t TextIndex;
typedef SkRange<size_t> TextRange;
const SkRange<size_t> EMPTY_TEXT = EMPTY_RANGE;

struct Block {
    Block() = default;
    Block(size_t start, size_t end, const TextStyle& style) : fRange(start, end), fStyle(style) {}
    Block(TextRange textRange, const TextStyle& style) : fRange(textRange), fStyle(style) {}

    void add(TextRange tail) {
        SkASSERT(fRange.end == tail.start);
        fRange = TextRange(fRange.start, fRange.start + fRange.width() + tail.width());
    }

    TextRange fRange = EMPTY_RANGE;
    TextStyle fStyle;
};


typedef size_t BlockIndex;
typedef SkRange<size_t> BlockRange;
const size_t EMPTY_BLOCK = EMPTY_INDEX;
const SkRange<size_t> EMPTY_BLOCKS = EMPTY_RANGE;

struct Placeholder {
    Placeholder() = default;
    Placeholder(size_t start, size_t end, const PlaceholderStyle& style, const TextStyle& textStyle,
                BlockRange blocksBefore, TextRange textBefore)
            : fRange(start, end)
            , fStyle(style)
            , fTextStyle(textStyle)
            , fBlocksBefore(blocksBefore)
            , fTextBefore(textBefore) {}

    TextRange fRange = EMPTY_RANGE;
    PlaceholderStyle fStyle;
    TextStyle fTextStyle;
    BlockRange fBlocksBefore;
    TextRange fTextBefore;
};

}  // namespace textlayout
}  // namespace skia

#endif  // TextStyle_DEFINED
